home *** CD-ROM | disk | FTP | other *** search
/ Compendium Deluxe 2 / LSD and 17bit Compendium Deluxe - Volume II.iso / a / prog / cprog / metre.lha / metre.doc < prev    next >
Encoding:
Text File  |  1994-09-07  |  17.5 KB  |  437 lines

  1. Metre v1.08
  2. Copyright (c) 1993,1994 by Paul Long All rights reserved.
  3.  
  4. Paul Long                                              September 7, 1994
  5.  
  6. Internet: Paul_Long@ortel.org
  7.           pci!plong@ormail.intel.com
  8.           72607.1506@compuserve.com
  9. CompuServe: 72607,1506
  10.  
  11.  
  12. Introduction
  13. ------------
  14. Metre is a portable rule-based software-metrics tool for Standard C.
  15. It reports the following metrics for each function in a C source file:
  16.  
  17.          McCabe's Cyclomatic Complexity Index
  18.          Lines of code
  19.          Lines of executable code
  20.          Lines of comments
  21.          Lines of whitespace
  22.          Number of statements
  23.          Depth
  24.  
  25. It also prints warnings if it encountered a goto or continue statement
  26. or multiple return statements in a function. After all functions are
  27. processed, it reports the following metrics for the entire file:
  28.  
  29.          McCabe's Cyclomatic Complexity Index
  30.          Lines of code
  31.          Lines of executable code
  32.          Lines of comments
  33.          Lines of whitespace
  34.          Number of statements
  35.          Number of functions
  36.  
  37. Maximum, minimum, average, and total metrics are provided where
  38. appropriate. Metre also prints a warning if it encountered indention
  39. accompished by both spaces _and_ tabs.
  40.  
  41. Prior to generating the metrics, Metre prints an "Explanation and
  42. Guidelines" section that, among other things, explains the output
  43. notation. To surpress this, use the -Q comand-line option. This does not
  44. appear in the normal usage information that Metre displays when no
  45. command-line arguments are given. This is because this option is
  46. processed by a rule (see the Modifying Metre section) and not by the
  47. parser engine.
  48.  
  49. If you think that some of the diagnostics that Metre generates are
  50. unreasonable, such as warning about gotos, don't worry--you can
  51. easily change how Metre behaves if you have a Standard C compiler (see
  52. the Modifying Metre section).
  53.  
  54.  
  55. Building Metre
  56. --------------
  57. Metre is distributed as a set of lex, yacc, and Standard C source files.
  58. From these, you should be able to build an executable for any
  59. environment that has a Standard C compiler. If you have lex and yacc,
  60. you can also modify the lex and yacc specifications in scan.l and
  61. gram.y, respectively, and regenerate the parser's C source files. Just
  62. in case you don't have lex/yacc, I've provided the lex_yy.c and ytab.c
  63. that were generated with MKS lex/yacc.
  64.  
  65. For example, under MS-DOS using MKS lex/yacc and the Borland C compiler,
  66. the Metre executable can be built with these commands:
  67.  
  68.    yacc -d gram.y
  69.    lex scan.l
  70.    bcc -w-rch -w-ccc -w-eff -emetre.exe rules.c ytab.c lex_yy.c
  71.  
  72. Without the -w command-line options, I get warnings about unreachable
  73. code, condition always being true/false, and code having no effect.
  74. These warnings can be safely ignored.
  75.  
  76. If you don't have lex/yacc, skip the first two lines and just compile
  77. and link the provided lex_yy.c and ytab.c. The -d tells yacc to generate
  78. a token header file that the lex specification includes. Being an MS-DOS
  79. implementation, this header file is called ytab.h instead of the normal
  80. UNIX y.tab.h. So, if you use a yacc that generates y.tab.h, you'll need
  81. to either rename it to ytab.h or change the #include directive in scan.l
  82. to include y.tab.h instead of ytab.h.
  83.  
  84. For example, under UNIX using AT&T lex/yacc and acc, the Metre
  85. executable can be built with these commands:
  86.  
  87.    yacc -d gram.y
  88.    mv y.tab.h ytab.h
  89.    lex scan.l
  90.    acc -o metre rules.c y.tab.c lex.yy.c
  91.  
  92. If you don't have lex/yacc, compile and link the provided lex_yy.c and
  93. ytab.c using the following command:
  94.  
  95.    acc -w -o metre rules.c ytab.c lex_yy.c
  96.  
  97. Without the -w command-line option, I get a bunch of warnings about -1
  98. (~0) not fitting as an initializer for yy_check[] in lex_yy.c. I also
  99. get a few signed-vs-unsigned-chars warnings regarding yytext[]. These
  100. warnings can be safely ignored. However, perhaps you should not use -w
  101. the first time just in case there are other warnings that this would
  102. suppress.
  103.  
  104.  
  105.  
  106. Test drive
  107. ----------
  108. To test Metre once you have built the executable from the source, run it
  109. against the accompanying rules.c source file with the following command:
  110.  
  111.          metre -DBOOLEAN=int rules.c
  112.  
  113. This instructs Metre to replace all occurences of BOOLEAN in rules.c
  114. with int so that you don't need to run a preprocessor first. It then
  115. displays various metrics about the code contained in rules.c.
  116.  
  117.  
  118. Preprocessor
  119. ------------
  120. Metre does not perform preprocessing, so you should typically run your
  121. source through a preprocessor first and then run the output of the
  122. preprocessor into Metre. For example, with Borland's MS-DOS tool set:
  123.  
  124.          cpp -P- myfile.c
  125.  
  126. (The -P- tells the preprocessor not to include its non-ANSI-C line
  127. numbers in its output.) In this case, the output would be called
  128. myfile.i. This is the file that should be used with Metre, as in the
  129. following:
  130.  
  131.          metre myfile.i
  132.  
  133. Since Metre output refers to the input file by name and this is may not
  134. be the name of the original source file (for example, you ran the
  135. preprocessor on it but the preprocessor didn't generate line
  136. directives), the -S option can be used to specify the name of the
  137. original file, as in -Smyfile.c. Note that this option can be repeated
  138. so that each one specifies a substitute file name for each input file,
  139. respectively. If you entered the following line:
  140.  
  141.          metre -Sa.c -Sb.c a.i b.i
  142.  
  143. the Metre output would use the name, "a.c", for the first file (a.i)
  144. and "b.c" for the second file (b.i).
  145.  
  146. Metre refers to lines by number in its input file unless it encounters a
  147. line directive, e.g., #line 5 "startup.c" or # 503 "main.c" 2. It
  148. replaces the current line number and file name with whatever is
  149. specified in the line directive. Some preprocessors can place line
  150. directives in the output file that relate back to the original source
  151. file. If yours does, you probably want to use this option.
  152.  
  153. While Metre recognizes C comments, for example, /* a comment */, it does
  154. not recognize C++ comments--those that begin with //.
  155.  
  156. As you may have noticed in the introduction section, Metre counts and
  157. displays lines of comments. However, if you run your program through a
  158. preprocessor, the preprocessor will have most likely stripped all
  159. comments from its output. Metre cannot count what is not there. What is
  160. the solution? Well, some preprocessors provide a command-line option
  161. that passes comments through. If yours has this capability, you probably
  162. want to use it.
  163.  
  164.  
  165. Output
  166. ------
  167. Use the -C command-line option to cause the parser to copy its input to
  168. its output, interspersing its reporting information with your source
  169. file. Use the -L option to redirect output to a file, as in: -Lout.txt.
  170. This is provided for environments that do not have command-line
  171. redirection, for example, VMS.
  172.  
  173.  
  174. Adaptibility
  175. -------------
  176. Metre ignores preprocessor directives (from # to end-of-line) other than
  177. line directives, so if your preprocessor passes any through, such as
  178. #pragmas, everything should be okay. This could also be useful if you
  179. only rely on the preprocessor for simple substitution. If so, you might
  180. be able to get away with running your C source file directly into Metre
  181. without being preprocessed. This is done in the example in the Quick
  182. Start section.
  183.  
  184. Since some C compilers, such as Intel's iRMX C compiler, accept the
  185. dollar sign ($) as a valid identifier character (and because it is not
  186. otherwise defined in C), Metre accepts identifiers with a dollar sign.
  187. They are valid anywhere a letter is, for example, $100, any$more, and
  188. mo$ are all valid identifiers to Metre.
  189.  
  190. The define command-line option allows you to perform simple
  191. substitutions within the input file. This may be enough to convert the
  192. extensions used by your compiler to something that Metre will understand
  193. as Standard-C syntax. For example, since Borland uses cdecl in its
  194. stdio.h file, I have to use the following command to get Metre to accept
  195. it:
  196.  
  197.          metre -Dcdecl= rules.i
  198.  
  199. This defines cdecl as a null string, effectively removing all
  200. occurrences from Metre's input stream. There may be a situation where a
  201. lexeme used in a declaration could be converted to another, as in the
  202. following:
  203.  
  204.          metre -Dselector=int foo.i
  205.  
  206.  
  207. Decision points
  208. ---------------
  209. There are a couple of situations in C where the presence of a decision
  210. point may be debatable (I assume you already know what a decision point
  211. is). Metre counts the conditional expression (?:) as a decision point,
  212. and it counts adjacent cases as one decision point. For an example of
  213. the latter, the following switch statement contains two decision points,
  214. not three.
  215.  
  216.          switch (a)
  217.          {
  218.          case 0:
  219.             dothis();
  220.             break;
  221.          case 1:
  222.          case 2:
  223.             dothat();
  224.             break;
  225.          }
  226.  
  227. Metre interprets decision points in this way so as to minimally
  228. influence the programmer's choice of constructs. If it did not count the
  229. conditional expression as a decision point, the programmer might be
  230. influenced to use confusing conditional expressions instead of if
  231. statements in order to obtain a lower CCI. Likewise, adjacent cases are
  232. analogous to the logical or operator (||). If Metre counted each case as
  233. a separate decision point, the programmer might avoid the switch
  234. statement in favor of an if statement with an expression that uses the
  235. or operator. For example,
  236.  
  237.          if (a == 1 || a == 2)
  238.  
  239.  
  240. Modifying Metre
  241. ---------------
  242. Like Abraxas' CodeCheck product, Metre is rule-based. Different behavior
  243. is obtained by simply modifying or replacing the rules in the rules.c
  244. file rather than modifying the more complex code of the parser engine
  245. (in scan.l and gram.y) that is the core of Metre. As a matter of fact,
  246. most of the software-metrics behavior described in this document is
  247. implemented with rules. Therefore, for your own good, make changes to
  248. rules.c if at all possible. Only tinker with gram.y and scan.l if the
  249. behavior you seek cannot be expressed with rules using the existing
  250. parser engine.
  251.  
  252. Note: The rules.c file contains rules that reflect my own programming
  253. sensibilities. For example, it generates a warning if a function has
  254. more than one return statement. If you don't like this, remove the rule.
  255.  
  256. The parser engine can be used to construct other tools. Think of it as
  257. a basic, easy-to-use parser for Standard C. As an example, by only
  258. providing a few rules and not modifying the parser at all, I built a
  259. special version of the UNIX tool, diff, that has the dual granularity
  260. of text lines and C functions. My version indicates whether a function
  261. has been removed or added, and if a function has been modified, it
  262. names it and then generates the normal diff output showing which lines
  263. have been added, removed, or modified.
  264.  
  265. There must always be a rules() function linked with the parser. It
  266. contains a set of "rules" that define how the resulting executable
  267. behaves. With no rules, for example, void rules(void){}, the result is
  268. just a C syntax checker. Rules are of the following form:
  269.  
  270.          if (<trigger>)
  271.             <action>
  272.  
  273. Trigger is an expression of terms taken from the structs and functions
  274. declared in the provided metre.h header file. See the rules.c file for
  275. examples. Generally, trigger should include a test that the particular
  276. construct has begun or ended, but that is not always possible. For
  277. example,
  278.  
  279.          if (stm.end)
  280.             ...
  281.  
  282. See the metre.h file for other possibilities.
  283.  
  284. A rule should NEVER have an else, although the action may contain if's
  285. with else's. The rule's else would effectively be executed ALL THE TIME
  286. except when the trigger occurs. Not a pretty sight.
  287.  
  288. Action is any statement, including the compound statement, {...}. The
  289. following are examples taken from the provided rules.c file:
  290.  
  291.          if (keyword("goto"))
  292.             ++fcn_gotos;
  293.  
  294.          if (fcn.begin)
  295.          {
  296.             fcn_gotos = 0;
  297.             fcn_continues = 0;
  298.             fcn_returns = 0;
  299.             fcn_depth_max = 0;
  300.          }
  301.  
  302.  
  303. Bugs
  304. ----
  305. 1. In Metre, all typedef definitions have file scope. I have never seen
  306. anyone use block scoped typedef definitions, so I doubt whether this
  307. will catch anyone. Here is an example of what you cannot do:
  308.  
  309.                      typedef int a;
  310.                      main()
  311.                      {
  312.                         typedef char *a;
  313.                      }
  314.  
  315. Metre declares a syntax error at the second "a" because it thinks that
  316. it is a type specifier, like "int," and not an identifier.
  317.  
  318. typedef scoping wouldn't be that hard to implement. For example,
  319. replace the current symbol table with a stack. Mark the beginning of a
  320. new scope. Push typedef names onto the stack. Search the table from the
  321. most recently pushed names. At the end of a scope, pop all typedef
  322. names off up to the first-encountered scope mark.
  323.  
  324.  
  325. History
  326. -------
  327. 1.08  9/3/94   First distribution with full source.
  328.                Valiant attempt to make lex specification portable across
  329.                   different versions of lex. (yacc specification should
  330.                   also be portable.)
  331.                Added compile-time ability to turn off input buffering.
  332.                If present, Use # [line] <n> ["<file name>"] instead of
  333.                   physical line number and file name so that the
  334.                   original source before translation is reflected.
  335. 1.07  3/7/94   Bug fixed where function name would be overridden by a
  336.                   function declaration within the function.
  337. 1.06  2/21/94  Redundant -U command-line option removed. For example,
  338.                   -Uapple is equivalent to -Dapple=. Metre has no
  339.                   predefined constants to undefine.
  340.                In addition to the original MS-DOS version, versions for
  341.                   VMS and Sun UNIX are now provided.
  342. 1.05  2/1/94   Obsolete -T command-line option removed.
  343.                Rules added to report number of comment lines (if not
  344.                   preprocessed out, of course).
  345.                Bug fixed where identifiers in the function-declarator
  346.                   parameter list were incorrectly interpreted as typedef
  347.                   identifiers. Metre now recognizes the following
  348.                   without a syntax error:
  349.                            typedef (*SIM_BODY_FP)(int status);
  350.                            struct {
  351.                               int status;
  352.                            };
  353. 1.04  1/29/94  First public availability.
  354.  
  355.  
  356. Feedback
  357. --------
  358. I would like to hear what you think about Metre. What enhancements would
  359. you like to see?  What bugs have you found?  The highest CCI I have
  360. encountered is 90. Have you found a higher one? Please send e-mail to
  361. one of the addresses listed at the top of this document.
  362.  
  363.  
  364. Possible enhancements
  365. ---------------------
  366. Here are some enhancements that I have thought about implementing in
  367. Metre.
  368.  
  369. Features
  370. - Recognize C++ comments.
  371. - Distinguish between executable and non-executable lines.
  372. - Detect possibly incorrect else binding based on indention.
  373. - Detect no return at end of function.
  374. - Over-all quality index, canned, then user-defined in place of CCI.
  375. - Optionally, adjust CCI downward for long, shallow switch statements or
  376.    have separate metric to mitigate CCI.
  377. - Count commented-code lines.
  378. - Incorporate more of CodeCheck's triggers and general capabilities.
  379.  
  380. Infrastructure
  381. - Perform block scoping on typedefs.
  382. - Do not display line number for module & project messages.
  383. - Dynamic buffer allocation and reallocation on as-needed basis.
  384. - Provide Standard C preprocessor. Integrated, with optional output, at
  385.    least for debugging.
  386. - Accept input from standard input if no files are specified on command
  387.    line. This would allow piped input from a previous command.
  388.  
  389.  
  390. Trademarks
  391. ----------
  392. Trademarks are the property of their respective owners.
  393.  
  394.  
  395. Disclaimer
  396. ----------
  397. THE METRE SOURCE AND INCLUDED ACCESSORY FILES ("THE SOFTWARE") ARE NOT
  398. WARRANTED IN ANY WAY TO BE SUITABLE FOR YOUR SITUATION. YOU USE THE
  399. SOFTWARE ENTIRELY AT YOUR OWN RISK.
  400.  
  401.  
  402. Licensing agreement
  403. -------------------
  404. The Metre source and included accessory files ("the software") are the
  405. copyrighted work of their author, Paul Long. The portions of the
  406. software present in all descendant manifestations of the software are
  407. also the copyright of Paul Long. All rights under US and international
  408. copyright law are reserved. You are hereby granted a limited license at
  409. no charge to use the software and to make copies of and distribute said
  410. copies of the software, as long as:
  411.  
  412. 1. Such use is not primarily for profit. "For profit" use is defined as
  413. primary use of the program whereby the user accrues financial benefit or
  414. gain directly from use of the software; for example, a commercial
  415. product that is descendant from the software, or a consultant performing
  416. code analysis for a client with the software. "Primary use" is defined
  417. as the major use of the program, or the primary reason for acquiring and
  418. using the program.
  419.  
  420. 2. Such copies maintain the complete set of files as provided in the
  421. original distribution set, with no changes, deletions or additions, or,
  422. in the case of descendant manifestations, the notice, "Metre Copyright
  423. (c) 1993,1994 by Paul Long All rights reserved.", appears without the
  424. quotation marks when the program is executed. The archive storage format
  425. may be changed as long as the rest of this condition is met.
  426.  
  427. 3. Copies are not distributed by any person, group or business that has
  428. as its primary purpose the distribution of free and "shareware" software
  429. by any means magnetic, electronic or in print, for profit. BBS
  430. distribution is allowed as long as no fee is charged specifically for
  431. this software. Bona fide non-profit user's groups, clubs and other
  432. organizations may copy and distribute the software as long as no charge
  433. is made for such service beyond a nominal disk/duplication fee not to
  434. exceed $5.00. For-profit organizations or businesses wishing to
  435. distribute the software must contact the author for licensing
  436. agreements.
  437.